/**
 * 
 */
package edu.cmu.lti.lm.dt;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;

/**
 * @author qing
 *
 */
public class EntryCollection extends LinkedList<Entry> {

	private static String sent_begin = "<s>";
	/**
	 * 
	 */
	private static final long serialVersionUID = 852808116524412821L;
	
	public void readAllEntry(InputStream str) throws IOException{
		BufferedReader rd = new BufferedReader(new InputStreamReader(str));
		readAllEntry(rd);
		
	}
	
	public void readAllEntry(BufferedReader rd) throws IOException{
		String line;
		while((line = rd.readLine())!=null){
			String[] strs = line.split("\\s+");
			History hist = new History();
			hist.add(sent_begin);
			for(int i=0;i<strs.length;i++){
				Entry ent = new Entry(strs[i],hist);
				this.add(ent);
				hist.add(strs[i]);
				Iterator<Feature> it = Entry.getFeat().iterator();
				while(it.hasNext()){
					it.next().addValue(hist);
				}
			}
		}
	}
	
	/**
	 * Devide the set into two, using the question 
	 * @param q The question
	 * @return Postive set (first) and negative set (second)
	 */
	public Pair<EntryCollection,EntryCollection>
		divide(Question q){
		EntryCollection ePos = new EntryCollection();
		EntryCollection eNeg = new EntryCollection();
		Iterator<Entry> ent = iterator();
		while(ent.hasNext()){
			Entry e = ent.next();
			if(q.classify(e)){
				ePos.add(e);
			}else{
				eNeg.add(e);
			}
		}
		return new Pair<EntryCollection,EntryCollection>(ePos,eNeg);
	}
	
	protected static Map<String, Double> wordDistribute(EntryCollection e){
		Map<String, Double> wd = new HashMap<String,Double>();
		Iterator<Entry> it = e.iterator();
		while(it.hasNext()){
			Entry en = it.next();
			if(wd.containsKey(en.getWord())){
				wd.put(en.getWord(), wd.get(en.getWord()) + 1);
			}else{
				wd.put(en.getWord(), 1.0);
			}
		}
		double totalWord = e.size();
		Iterator<Map.Entry<String,Double>> i = wd.entrySet().iterator();
		
		while(i.hasNext()){
			Map.Entry<String,Double> v = i.next();
			v.setValue(v.getValue() / totalWord);
		}
		return wd;
	}
	
	private static double p2 = Math.log(2);
	
	public Pair<Double,Pair<EntryCollection,EntryCollection>>
		getConditionalEntropy(Question q){
		Pair<EntryCollection,EntryCollection> e = divide(q);
		EntryCollection Positive = e.getFirst();
		EntryCollection Negative = e.getSecond();
		double prPos =  (double)Positive.size() / (Positive.size()+Negative.size());
		double prNeg = 1 - prPos;
		Map<String, Double> wdPos = wordDistribute(Positive);
		Map<String, Double> wdNeg = wordDistribute(Negative);
		double posEntropy = 0 ;
		Iterator<Double> dbl = wdPos.values().iterator();
		while(dbl.hasNext()){
			double p = dbl.next();
			posEntropy += p * Math.log(p) / p2;
		}
		double negEntropy = 0 ;
		dbl = wdNeg.values().iterator();
		while(dbl.hasNext()){
			double p = dbl.next();
			negEntropy += p * Math.log(p) / p2;
		}
		double finals = (-1)* (prPos * posEntropy + prNeg * negEntropy);
		return new Pair<Double,Pair<EntryCollection,EntryCollection>>(finals,e);
	}
	
	public double getConditionalEntropy(){
		Map<String, Double> wdPos = wordDistribute(this);
		double posEntropy = 0 ;
		Iterator<Double> dbl = wdPos.values().iterator();
		while(dbl.hasNext()){
			double p = dbl.next();
			posEntropy += p * Math.log(p) / p2;
			}
		
		double finals = (-1)* ( posEntropy );
		return finals;
		}

}
